import { Bi } from "@/module-00-codebase/pkg-01-container";
import { Field } from "@/module-00-codebase/pkg-08-entity/field-entity";
export class Validator {
  public async validate(type: string, field: Field, data: any) {
    for (let i = 0; i < validatorMap.length; i++) {
      if (validatorMap[i].test(type)) {
        return await validatorMap[i].vali(type, field, data);
      }
    }
    return "";
  }
}
export interface ValiMatcher {
  test: (type: string) => boolean;
  vali: (type: string, field: Field, data: any) => Promise<string>;
}
export const validatorMap: Array<ValiMatcher> = [];
validatorMap.push({
  test: type => "notNull" === type,
  vali: async (type, field, data) => (Bi.utils.isNullOrEmpty(field.getter(data)) ? await Bi.i18n.translate("ZWF cannot be empty.", field.label) : "")
});
validatorMap.push({
  test: type => type.indexOf("maxLen") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (!!value && value.length > len) return await Bi.i18n.translate("Maximum word limit of ZWF is ZWF", field.label, len);
    else return "";
  }
});
validatorMap.push({
  test: type => type.indexOf("maxSize") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (!!value && value.length > len) return await Bi.i18n.translate("Maximum size limit of ZWF is ZWF", field.label, len);
    else return "";
  }
});
validatorMap.push({
  test: type => type.indexOf("maxNmbr") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (!!value && value.length > len) return await Bi.i18n.translate("Maximum limit of ZWF is ZWF", field.label, len);
    else return "";
  }
});
validatorMap.push({
  test: type => type.indexOf("maxDate") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (!!value && value.length > len) return await Bi.i18n.translate("Maximum date limit of ZWF is ZWF", field.label, Bi.dateFormat.format(new Date(len)));
    else return "";
  }
});

validatorMap.push({
  test: type => type.indexOf("minLen") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (value.length < len) return await Bi.i18n.translate("Minimum word limit of ZWF is ZWF", field.label, len);
    else return "";
  }
});
validatorMap.push({
  test: type => type.indexOf("minSize") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (!value || value.length < len) return await Bi.i18n.translate("Minimum size limit of ZWF is ZWF", field.label, len);
    else return "";
  }
});
validatorMap.push({
  test: type => type.indexOf("minNmbr") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (value.length < len) return await Bi.i18n.translate("Minimum limit of ZWF is ZWF", field.label, len);
    else return "";
  }
});
validatorMap.push({
  test: type => type.indexOf("minDate") === 0,
  vali: async (type, field, data) => {
    let len = Number.parseFloat(type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")));
    let value: string = field.getter(data);
    if (value.length < len) return await Bi.i18n.translate("Minimum date limit of ZWF is ZWF", field.label, Bi.dateFormat.format(new Date(len)));
    else return "";
  }
});
validatorMap.push({
  test: type => type === "email",
  vali: async (type, field, data) => {
    let value: string = field.getter(data);
    if (emailExp.test(value)) return "";
    else return await Bi.i18n.translate("ZWF is not a legal email address", value || "");
  }
});
validatorMap.push({
  test: type => type === "pswd",
  vali: async (type, field, data) => {
    let value: string = field.getter(data);
    if (pswdExp.test(value)) return "";
    else return await Bi.i18n.translate("ZWF is not a legal password", value);
  }
});
validatorMap.push({
  test: type => type.indexOf("notNullIfAbsent") === 0,
  vali: async (type, field, data) => {
    const value = field.getter(data);
    if (Bi.utils.isNullOrEmpty(value)) {
      const anotherProperty = type.substring(type.indexOf("(") + 1, type.lastIndexOf(")"));
      const anotherField = Bi.propertyDescription.get(field.declaringType, anotherProperty);
      const anotherValue = anotherField.getter(data);
      if (Bi.utils.isNullOrEmpty(anotherValue)) {
        return await Bi.i18n.translate("ZWF or ZWF should not be empty.", field.label, anotherField.label);
      }
    }
    return "";
  }
});
// validatorMap.push({
//   test: type => type === "confirmPswd",
//   vali: async (type, field, data) => {
//     let value: string = field.getter(data);
//     let pswd = Bi.propertyDescription.get(field.declaringType, "password").getter(data);
//     if (value !== pswd) {
//       return "Confirm password is not the same as the password.";
//     } else {
//       return "";
//     }
//   }
// });
validatorMap.push({
  test: type => type === "cron",
  vali: async (type, field, data) => {
    if (cronExp.test(field.getter(data))) {
      return "";
    } else {
      return "Illegal cron expression";
    }
  }
});
validatorMap.push({
  test: type => type.indexOf("unique") === 0,
  vali: async (type, field, data) => {
    const index = type.indexOf("(");
    const value = field.getter(data);
    const properties = index > 0 ? type.substring(index + 1, type.lastIndexOf(")")).split(",") : [];
    const rootPrefix = field.name.indexOf(".") > 0 ? field.name.substring(0, field.name.lastIndexOf(".")) : "";
    const endingFix = field.name.indexOf(".") > 0 ? field.name.substring(field.name.lastIndexOf(".") + 1) : field.name;
    const validateProperties = properties.map(p => {
      const propField = Bi.propertyDescription.get(field.declaringType, p);
      propField.name = !!rootPrefix ? `${rootPrefix}.${propField.name}` : propField.name;
      return { [p]: propField.getter(data) };
    });
    const idField: Field = Bi.propertyDescription.get(field.declaringType, "id");
    idField.name = !!rootPrefix ? `${rootPrefix}.${idField.name}` : idField.name;
    const validateData = validateProperties.reduce((a, b) => Object.assign(a, b), { id: idField.getter(data), [endingFix]: field.getter(data) });
    const pntType: string = (!!rootPrefix ? Bi.propertyDescription.get(field.declaringType, rootPrefix).orefType : field.declaringType) as string;
    const res = await Bi.rpc.send({ loading: false, action: "exists", resourceName: Bi.utils.lowerFirst(pntType), data: validateData });
    const msg = res?.data?.exists === false ? "" : await Bi.i18n.translate("ZWF [ZWF] has already been used.", field.label, value);
    return msg;
  }
});
export const emailExp = new RegExp("^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$");
export const pswdExp = new RegExp(".*");
export const cronExp = (() => {
  const djx = (n: string) => `(\\*)|(${n}(,${n})*)|(${n}(\\-${n})?(/\\d+)?)`;
  const lc = (n: string) => djx(n) + `|(\\?)|(${n}W)(${n}L)(${n}C)`;
  const lcw = (n: string) => lc(n) + `|(${n}W)`;
  const lca = (n: string) => lc(n) + `|(${n}#[1-4])`;
  const sq = (fst: string, ns: string[], lst: string) => `^(${ns.reduce((a, b) => `(${a})\\s(${b})`, fst)}(\\s(${lst}))?)$`;
  const ss = djx("([1-5]?[0-9])");
  const mm = djx("([1-5]?[0-9])");
  const HH = djx("((1?[0-9])|(2[0-3]))");
  const dd = lcw("([1-9]|([1-2][0-9])|(3[0-1]))");
  const MM = djx("([1-9]|(1[0-2]))");
  const wk = lca("[1-7]");
  const yy = djx("((19[7-9][0-9])|(20[0-9][0-9]))");
  return new RegExp(sq(ss, [mm, HH, dd, MM, wk], yy));
})();
